package chloroform.json.comp

import chloroform.json.JsonBuilderFactory
import argo.jdom._
import java.text.SimpleDateFormat
import java.util.Date

/**
 * Created by IntelliJ IDEA.
 * User: zslajchrt
 * Date: 15.5.11
 * Time: 13:45
 */

class ValueHolder[T](var value: T)

abstract case class JsonProperty[T, N <: JsonNode](defaultValue: ValueHolder[T]) extends JsonBuilderFactory[N] {
  private[this] var valueHolder: ValueHolder[T] = defaultValue;

  def value: T = {
    if (valueHolder == null) {
      error("no value set")
    } else
      valueHolder.value
  }

  def apply(newValue: T) {
    valueHolder = new ValueHolder[T](newValue);
  }

  def apply(): T = value

  def <=(value: T) {
    apply(value)
  }

  def createBuilder(): Option[JsonNodeBuilder[N]] = {
    if (valueHolder == null)
      None
    else
      Some(createPropertyJsonBuilder());
  }

  def reset() {
    valueHolder = null;
  }

  def createPropertyJsonBuilder(): JsonNodeBuilder[N]
}

object string {
  def apply(value: String = null): stringProp = {
    if (value == null)
      stringProp(null)
    else
      stringProp(new ValueHolder[String](value))
  }
}
case class stringProp(valHolder: ValueHolder[String]) extends JsonProperty[String, JsonStringNode](valHolder) {
  def createPropertyJsonBuilder(): JsonNodeBuilder[JsonStringNode] =
    JsonNodeBuilders.aStringBuilder(value)
}

object boolean {
  def apply(): booleanProp = {
    booleanProp(null)
  }

  def apply(value: Boolean): booleanProp = {
    booleanProp(new ValueHolder[Boolean](value))
  }
}
case class booleanProp(valHolder: ValueHolder[Boolean]) extends JsonProperty[Boolean, JsonNode](valHolder) {
  def createPropertyJsonBuilder(): JsonNodeBuilder[JsonNode] =
    if (value) {
      JsonNodeBuilders.aTrueBuilder()
    } else {
      JsonNodeBuilders.aFalseBuilder()
    }
}

object number {
  def apply(value: Number = null): numberProp = {
    if (value == null)
      numberProp(null)
    else
      numberProp(new ValueHolder[Number](value))
  }
}
case class numberProp(valHolder: ValueHolder[Number]) extends JsonProperty[Number, JsonNode](valHolder) {
  def createPropertyJsonBuilder(): JsonNodeBuilder[JsonNode] =
    JsonNodeBuilders.aNumberBuilder(value.toString)
}


object date {
  val dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")

  def apply(value: Date = null): dateProp = {
    if (value == null)
      dateProp(null)
    else
      dateProp(new ValueHolder[Date](value))
  }
}
case class dateProp(valHolder: ValueHolder[Date]) extends JsonProperty[Date, JsonStringNode](valHolder) {
  def createPropertyJsonBuilder(): JsonNodeBuilder[JsonStringNode] =
    JsonNodeBuilders.aStringBuilder(date.dateFormat.format(value))
}

object struct {
  def apply(): structProp[JsonBuilderFactory[JsonRootNode]] = {
    apply(null)
  }
  def apply[T <: JsonBuilderFactory[_ <: JsonNode]](refBuilderFact: T): structProp[T] = {
    if (refBuilderFact == null)
      structProp[T](null)
    else
      structProp[T](new ValueHolder(refBuilderFact))
  }
}

case class structProp[T <: JsonBuilderFactory[_ <: JsonNode]](valHolder: ValueHolder[T])
  extends JsonProperty[T, JsonNode](valHolder) {

  def createPropertyJsonBuilder(): JsonNodeBuilder[JsonNode] =
    value.createBuilder() match {
      case None => JsonNodeBuilders.aNullBuilder
      case Some(refBuilder) => refBuilder.asInstanceOf[JsonNodeBuilder[JsonNode]]
    }
}

object array {
  def apply[E](elemProp: JsonProperty[E, _ <: JsonNode]): arrayProp[E] = {
    //elem
    arrayProp(elem => {
      elemProp.reset()
      elemProp <= elem
      elemProp
    })
  }
}

case class arrayProp[E](elem2factory: (E) => JsonBuilderFactory[_ <: JsonNode])
  extends JsonProperty[List[E], JsonRootNode](null) {

  def createPropertyJsonBuilder(): JsonNodeBuilder[JsonRootNode] = {

    val arrayBuilder = JsonNodeBuilders.anArrayBuilder

    value.foreach(element => {
      val elementBuilderFactory: JsonBuilderFactory[_ <: JsonNode] = elem2factory(element)
      elementBuilderFactory.createBuilder() match {
        case None =>
        case Some(elemBuilder) => arrayBuilder.withElement(elemBuilder)
      }

    })

    arrayBuilder
  }
}

class JsonComposite extends JsonBuilderFactory[JsonRootNode] {
  def createBuilder(): Option[JsonNodeBuilder[JsonRootNode]] = {

    val propertyBuilderFactoryGetters = getClass.getMethods.filter(m => {
      classOf[JsonBuilderFactory[_]].isAssignableFrom(m.getReturnType) && m.getParameterTypes.length == 0
    })

    val propertyBuilderFactoryPairs: Array[(String, JsonBuilderFactory[JsonNode])] =
      propertyBuilderFactoryGetters.map(method => {
        (method.getName, method.invoke(JsonComposite.this).asInstanceOf[JsonBuilderFactory[JsonNode]])
      })

    val objectBuilder: JsonObjectNodeBuilder = JsonNodeBuilders.anObjectBuilder
    propertyBuilderFactoryPairs.foreach(pair => {
      pair._2.createBuilder() match {
        case None =>
        case Some(propNodeBuilder) => {
          val fieldName: String = pair._1
          objectBuilder.withField(fieldName, propNodeBuilder)
        }
      }
    })

    Some(objectBuilder)
  }
}

class MapJsonComposite(propFactories: Map[String, JsonBuilderFactory[JsonNode]]) extends JsonBuilderFactory[JsonRootNode] {
  def createBuilder(): Option[JsonNodeBuilder[JsonRootNode]] = {

    val objectBuilder: JsonObjectNodeBuilder = JsonNodeBuilders.anObjectBuilder
    propFactories.foreach(pair => {
      pair._2.createBuilder() match {
        case None =>
        case Some(propNodeBuilder) => {
          val fieldName: String = pair._1
          objectBuilder.withField(fieldName, propNodeBuilder)
        }
      }
    })

    Some(objectBuilder)
  }
}
















